﻿using System;

namespace Encloser.Packed
{
    public class Result
    {
        protected readonly bool success;
        protected readonly string message;
        protected readonly Exception exception;

        public bool IsSuccess { get => success; }

        public readonly static Result OK = new Result(true, null, null);
        public readonly static Result FAIL = new Result(false, null, null);
        public readonly static Result INVALID_PARAMETERS = new Result(false, "参数不合法", null);

        protected Result(bool success, string message = null, Exception exception = null)
        {
            this.success = success;
            this.message = message;
            this.exception = exception;
        }

        public Result Try(Action action)
        {
            if (success)
            {
                try
                {
                    action?.Invoke();
                }
                catch (Exception e)
                {
                    return Failed(e);
                }
            }
            return this;
        }

        public Result<V> Try<V>(Func<V> func)
        {
            if (success)
            {
                if (func == null) return Result<V>.INVALID_PARAMETERS;
                try
                {
                    return Result<V>.Success(func.Invoke());
                }
                catch (Exception e)
                {
                    return Result<V>.Failed(e);
                }
            }
            return Result<V>.Failed(message, exception);
        }

        public Result<V> Try<V>(Func<Result<V>> func)
        {
            if (success)
            {
                if (func == null) return Result<V>.INVALID_PARAMETERS;
                try
                {
                    return func.Invoke();
                }
                catch (Exception e)
                {
                    return Result<V>.Failed(e);
                }
            }
            return Result<V>.Failed(message, exception);
        }

        public Result Catch(Action action)
        {
            if (!success) action?.Invoke();
            return this;
        }

        public Result Catch(Action<string, Exception> action)
        {
            if (!success) action?.Invoke(message, exception);
            return this;
        }

        public Result Catch(Action<Result> action)
        {
            if (!success) action?.Invoke(this);
            return this;
        }

        public Result<V> Catch<V>()
        {
            if (!success) return Result<V>.Failed(message, exception);
            return Result<V>.Success(default);
        }

        public void Finally(Action action)
        {
            action?.Invoke();
        }

        public static Result Failed(Exception exception = null)
        {
            return new Result(false, exception?.Message, exception);
        }

        public static Result Failed(string message, Exception exception = null)
        {
            return new Result(false, message ?? exception?.Message, exception);
        }
    }
}
